import os
import importlib
from pathlib import Path
import json
import time
import re

from absl import app, flags
from coin2.support.product_info.ico import add_ico_symbol, ioc_symbol_clear

non_pi_py_scripts = [
    'run.py',
    'product_info_holder.py',
    'ico.py',
    "spot_pancakeswap_v2.py",
    "spot_uniswap_v3.py",
    "spot_uniswap_v3-arbitrum.py",
    "spot_uniswap_v2.py"
]
mods = []
d = 'python/coin2/support/product_info/'
for f in os.listdir(d):
  if os.path.isfile(d + f) and f.endswith('.py') and f not in non_pi_py_scripts:
    mods.append(Path(f).stem)

listing_trade_meas = ["Futures.Prex.v1", "Spot.Prex.v1"]


def add_extra_pi(pisf, extra):
  with open(pisf) as fd:
    doc = json.load(fd)
    fetched_time = doc['fetched_time']

  with open(extra) as fd:
    exdoc = json.load(fd)
  pis = doc["product_infos"]
  expis = exdoc["product_infos"]

  filter = set()
  for pi in pis:
    filter.add(pi["symbol"])

  for pi in expis:
    if pi["symbol"] not in filter:
      pi["fetched_time"] = fetched_time
      pis.append(pi)

  with open(pisf, 'w') as fd:
    json.dump(doc, fd, indent=2)


def process_listing_symbol(mea):
  if mea == "Futures.Prex.v1" or "Spot.Prex.v1":
    pi_path = "data/coin2/product_info/" + mea + ".json"
    extra = "python/coin2/support/product_info/prex/" + mea + "_extra.json"
    add_extra_pi(pi_path, extra)


huobi_mea_list = [
    'Spot.Huobi.v1',
    'Futures.Huobi.v1',
    'Futures.Huobi.v1-swap',
    'Futures.Huobi.v1-linear-swap',
]

binance_mea_list = [
    'Spot.Binance.v1',
    'Futures.Binance.v1',
    'Futures.Binance.v1-delivery',
]

okex_v5_mea_list = [
    'Futures.Okex.v5',
    'Futures.Okex.v5-swap',
]

bybit_futures_mea_list = [
    'Futures.Bybit.v2',
    'Futures.Bybit.v2-linear',
]

gateio_futures_mea_list = [
    'Futures.Gateio.v4',
    'Futures.Gateio.v4-delivery',
]

filter_out_meas = [
    'Spot.Coingecko.v3'
]


def merge(files, out, dir='data/coin2/product_info/'):
  out = dir + out
  mea = None
  lists = []
  for f in files:
    f = dir + f
    with open(f) as fd:
      doc = json.load(fd)
      mea = doc['mea']
      lists.extend(doc['product_infos'])

  doc = {
      "mea": mea,
      "product_infos": lists,
  }
  with open(out, 'w') as fd:
    json.dump(doc, fd, indent=2)


def gen_compat(mea):
  if 'ALL' == mea or mea in huobi_mea_list:
    merge(['Futures.Huobi.v1.json', 'Futures.Huobi.v1-swap.json', 'Spot.Huobi.v1.json',
           'Futures.Huobi.v1-linear-swap.json'],
          'old_compat/huobi.json')
  if 'ALL' == mea or mea in binance_mea_list:
    merge(
        ['Futures.Binance.v1.json', 'Spot.Binance.v1.json', 'Futures.Binance.v1-delivery.json'],
        'old_compat/binance.json')
  if 'ALL' == mea or mea in okex_v5_mea_list:
    merge(['Futures.Okex.v5.json', 'Futures.Okex.v5-swap.json'],
          'old_compat/okex_v5_futures.json')
  if 'ALL' == mea or mea in bybit_futures_mea_list:
    merge(['Futures.Bybit.v2.json', 'Futures.Bybit.v2-linear.json'],
           'old_compat/bybit_futures.json')
  if 'ALL' == mea or mea in gateio_futures_mea_list:
    merge(['Futures.Gateio.v4.json', 'Futures.Gateio.v4-delivery.json'],
          'old_compat/gateio_futures.json')


def transform(mea, pis):
  # infinity = float('inf')
  infinity = 1e9

  for pi in pis:
    if not isinstance(pi['price_ticksize'], list):
      pi['price_ticksize'] = [[0, infinity, pi['price_ticksize']]]
    if not isinstance(pi['qty_ticksize'], list):
      pi['qty_ticksize'] = [[0, infinity, pi['qty_ticksize']]]

    pi['price_ticksize'] = [{'from': t[0], 'to': t[1], 'value': t[2]} for t in pi['price_ticksize']]
    pi['qty_ticksize'] = [{'from': t[0], 'to': t[1], 'value': t[2]} for t in pi['qty_ticksize']]
    pi.pop('regex', None)

  return {
      'mea': mea,
      'fetched_time': time.time_ns(),
      'product_infos': pis,
  }

def refresh_pancakeswap_fetch_time():
  files = [
      "data/coin2/product_info/Spot.Pancakeswap.v2.json",
      "data/coin2/product_info/Spot.Uniswap.v3.json",
      "data/coin2/product_info/Spot.Uniswap.v2.json",
      "data/coin2/product_info/Options.Otc.v1.json",
      "data/coin2/product_info/Spot.Uniswap.v3-arbitrum.json"
  ]
  for f in files:
    with open(f) as fd:
      info = json.load(fd)
    ts = time.time_ns()
    info["fetched_time"] = ts
    for pi in info["product_infos"]:
      pi["fetched_time"] = ts
    with open(f, 'w') as fd:
      json.dump(info, fd, indent=2)


def asset_multiplier_check(pis, mea):
  filter_mea_list = []
  filler_base_list = ["C98"]
  if mea in filter_mea_list:
    return

  filter_dic = {"Futures.Bitget.v1": ["BGHOT10USDT_UMCBL"]}

  for pi in pis:
    if mea in filter_dic:
      if pi["native_symbol"] in filter_dic[mea]:
        continue

    if pi["base"] in filler_base_list:
      continue
    if "asset_multiplier" not in pi or "asset" not in pi:
      regex = "[0-9]{2,}"
      result = re.search(regex, pi["base"])
      if result:
        print("run.py: [{}] symbol {},  asset multiplier may not be 1.0, Please check it".format(mea, pi["symbol"]))

ftx_list = ["spot_ftx_v1", "futures_ftx_v1"]

def main(argv):
  target_mea = flags.FLAGS.mea
  for m in mods:
    try:
      m_e_a = [x.capitalize() for x in m.split('_')]
      m_e_a[-1] = m_e_a[-1].lower()
      mea = '.'.join(m_e_a)
      if mea in filter_out_meas:
        continue
      if 'ALL' != target_mea and mea != target_mea:
        continue
      if mea.count('.') != 2:
        raise Exception('there is a file whose filename is not mea: ' + m)
      
      if m in ftx_list:
        continue
      
      mod = importlib.import_module('coin2.support.product_info.' + m)
      pis = mod.generate()

      if mea.split('.')[0] == "Futures":
        asset_multiplier_check(pis, mea)

      # if mea.startswith('All'):
      #   futures = [pi for pi in pis if (pi['symbol'].count('.') == 2)]
      #   spot = [pi for pi in pis if pi['symbol'].count('.') == 1]
      #   with open(f'data/coin2/product_info/Spot.{m_e_a[1]}.{m_e_a[2]}.json', 'w') as f:
      #     json.dump(spot, f)
      #   with open(f'data/coin2/product_info/Futures.{m_e_a[1]}.{m_e_a[2]}.json', 'w') as f:
      #     json.dump(futures, f)
      # else:

      with open(f'data/coin2/product_info/{mea}.json', 'w') as f:
        json.dump(transform(mea, pis), f, indent=2)

      #clear listed symbol
      ioc_symbol_clear(mea, flags.FLAGS.coin_product_info_dir)
      # add ico symbol
      add_ico_symbol(mea, flags.FLAGS.coin_product_info_dir)
      # extra pi
      if mea in listing_trade_meas:
        process_listing_symbol(mea)

    except Exception as e:
      print("[%s] failed to fetch. error: %s" % (mea, e))
  gen_compat(target_mea)
  if target_mea == "ALL":
    refresh_pancakeswap_fetch_time()


# usage:
# ./pyrunner  ./python/coin2/support/product_info/run.py --mea=Futures.Binance.v1
#
if __name__ == '__main__':
  flags.DEFINE_string('mea', 'ALL', 'mea')
  flags.DEFINE_string('coin_product_info_dir', '../coin_product_info', 'coin product info dir')
  app.run(main)
